#include <stdlib.h>
#include <string.h>
#ifndef PRODUCT
#include <stdio.h>
#endif

#include "crypto/types.h"
#include "crypto/bits.h"
#include "crypto/errors.h"

bits *initBits(int len)
{
    if (len <= 0)
    {
        export(CODE_PARAMS_ERROR, "The param len should not be negative or zero!");
    }
    int size = (len + 7) / 8;
    bits *buffer = (bits *)malloc(sizeof(bits) + size);
    if (buffer == NULL)
    {
        export(CODE_OOM, "Malloc failed!");
    }
    buffer->len = len;
    return buffer;
}

bits *copyBits(bits *buffer)
{
    int size = (buffer->len + 7) / 8;
    bits *buffer2 = (bits *)malloc(sizeof(bits) + buffer->len + size);
    if (buffer == NULL)
    {
        export(CODE_OOM, "Malloc failed!");
    }
    memcpy(buffer2->data, buffer->data, size);
    return buffer2;
}

uint8 getBit(bits *buffer, int index)
{

    index += buffer->len;
    index %= buffer->len;

    if (index < 0)
    {
        index += buffer->len;
    }

    uint bytes = index / 8;
    byte result = buffer->data[bytes];
    index %= 8;
    result = result << index;
    result = result >> 7;
    return result;
}

uint8 getBitFromArray(byte *buffer, uint index)
{
    uint bytes = index / 8;
    byte result = buffer[bytes];
    index %= 8;
    result = result << index;
    result = result >> 7;
    return result;
}

void setBit(bits *buffer, int index, uint8 bit)
{
    index %= buffer->len;

    if (index < 0)
    {
        index += buffer->len;
    }

    uint bytes = index / 8;
    index %= 8;
    if (bit == 0)
    {
        buffer->data[bytes] &= ~orArray[index];
    }
    else
    {
        buffer->data[bytes] |= orArray[index];
    }
}

void setBitFromBytes(bits *buffer, byte *data, uint len)
{
    if (buffer == NULL || (buffer->len + 7) / 8 < len)
    {
        export(CODE_PARAMS_ERROR, "The first params buffer error!");
    }
    memcpy(buffer->data, data, len);
}

void rotateBits(bits *buffer, int step)
{
    step %= buffer->len;
    if (step < 0)
    {
        step += buffer->len;
    }
    int size = (buffer->len + 7) / 8;
    byte tmpBuf[size];
    memcpy(tmpBuf, buffer->data, size);
    for (int i = 0; i < buffer->len; i++)
    {
        setBit(buffer, i + step, getBitFromArray(tmpBuf, i));
    }
}

void operateBits(bits *dst, bits *res, BIT_OPT opt)
{
    int size;
    if (dst == NULL)
    {
        export(CODE_PARAMS_ERROR, "The param dst is NULL!");
    }
    size = (dst->len + 7) / 8;
    if (opt == BIT_NOT)
    {
        for (int i = 0; i < size; i++)
        {
            dst->data[i] = ~dst->data[i];
        }
        return;
    }
    if (res == NULL)
    {
        export(CODE_PARAMS_ERROR, "The param res is NULL!");
    }
    if (dst->len != res->len)
    {
        export(CODE_PARAMS_ERROR, "Len members are not same!");
    }
    switch (opt)
    {
    case BIT_XOR:
        for (int i = 0; i < size; i++)
        {
            dst->data[i] = dst->data[i] ^ res->data[i];
        }
        break;
    case BIT_OR:
        for (int i = 0; i < size; i++)
        {
            dst->data[i] = dst->data[i] | res->data[i];
        }
        break;
    case BIT_AND:
        for (int i = 0; i < size; i++)
        {
            dst->data[i] = dst->data[i] & res->data[i];
        }
        break;
    default:
        export(CODE_PARAMS_ERROR, "Bit operateor error!");
    }
}

#ifndef PRODUCT
void printBits(bits *buffer)
{
#ifdef PRINT_BIT_DETAIL
    int count = 0;
    printf("len: %u\n", buffer->len);
    printf("data:");
#endif
    for (int i = 0; i < buffer->len; i++)
    {
#ifdef PRINT_BIT_DETAIL
        if (i % 8 == 0)
        {
            printf(" ");
        }
#endif
        if (getBit(buffer, i))
        {
            printf("1");
#ifdef PRINT_BIT_DETAIL
            count++;
#endif
        }
        else
        {
            printf("0");
        }
    }
#ifdef PRINT_BIT_DETAIL
    printf("\n1 count: %d\n\n", count);
#else
    printf("\n");
#endif
}
#endif
